package io.kiki.sba.registry.client.provider;

import io.kiki.sba.registry.client.api.ClientConfig;
import io.kiki.sba.registry.client.remoting.Server;
import io.kiki.sba.registry.client.remoting.ServerManager;
import io.kiki.sba.registry.client.util.HttpClientUtils;
import io.kiki.sba.registry.client.util.ServerNodeParser;
import lombok.Getter;
import lombok.Setter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;

@Setter
@Getter
public class ServerManagerImpl implements ServerManager {


    public static final int MIN_RETRY_INTERVAL = 10000;

    private static final Logger logger = LoggerFactory.getLogger(ServerManagerImpl.class);

    private ClientConfig clientConfig;
    private Set<Server> serverSet;
    private SyncServerListThread syncServerListTask;

    private AtomicBoolean inited = new AtomicBoolean(false);


    public ServerManagerImpl(ClientConfig clientConfig) {
        this.clientConfig = clientConfig;
        this.serverSet = new HashSet<>();
        this.syncServerListTask = new SyncServerListThread(this, clientConfig);
    }


    @Override
    public List<Server> getServerList() {
        // init once
        if (inited.compareAndSet(false, true)) {
            this.syncServerListTask.start();
        }
        // sync query when server list is empty
        if (serverSet.isEmpty()) {
            syncServerList();
        }
        return new ArrayList<Server>(serverSet);
    }


    @Override
    public Server random() {
        List<Server> serverList = getServerList();
        if (null == serverList || serverList.size() == 0) {
            return null;
        }
        Random random = new Random();
        return serverList.get(random.nextInt(serverList.size()));
    }

    void syncServerList() {
        String url = String.format("http://%s:%d/api/servers/queryWithWeight", clientConfig.getRegistryEndpoint(), clientConfig.getRegistryEndpointPort());
        Map<String, String> params = new HashMap<String, String>();
        params.put("env", clientConfig.getEnv());
        params.put("zone", clientConfig.getZone());
        params.put("dataCenter", clientConfig.getDataCenter());
        params.put("appName", clientConfig.getAppName());
        params.put("instanceId", clientConfig.getInstanceId());
        try {
            String result = HttpClientUtils.get(url, params, clientConfig);
            if (null == result) {
                // when registry not support query with weight , go back
                url = String.format("http://%s:%d/api/servers/query", clientConfig.getRegistryEndpoint(), clientConfig.getRegistryEndpointPort());
                result = HttpClientUtils.get(url, params, clientConfig);
            }
            if (null != result) {
                String[] servers = result.split(";");
                Set<Server> set = new HashSet<Server>();
                for (String server : servers) {
                    try {
                        Server serverNode = ServerNodeParser.parse(server);
                        set.add(serverNode);
                    } catch (Exception e) {
                        logger.error("[serverManager] parse server node error, {}", server, e);
                    }
                }
                if (!set.equals(this.serverSet)) {
                    this.serverSet = set;
                    logger.info("[serverManager] update nodes success, {}", set);
                }
            }
        } catch (Exception e) {
            logger.error("[serverManager] get server list error", e);
        }
    }


}
